﻿import * as sfs from "fs";
import * as q from 'q';

export class Encoding {
    detectEncodingByBOM(file: string) {
        var me = this;
        return q.promise((resolve, reject) => {
            var totalBytes = 3;
            sfs.open(file, 'r', null, (err, fd) => {
                if (err) {
                    return reject("读取文件失败");
                }

                function end(err: Error, resultBuffer: NodeBuffer, bytesRead: number): void {
                    sfs.close(fd, closeError => {
                        if (closeError) {
                            return reject("关闭文件失败");
                        }

                        if (err && (<any>err).code === 'EISDIR') {
                            return reject("关闭文件失败");
                        }

                        resolve(me.detectEncodingByBOMFromBuffer(resultBuffer, bytesRead));
                    });
                }

                const buffer = new Buffer(totalBytes);
                let offset = 0;

                function readChunk(): void {
                    sfs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => {
                        if (err) {
                            return end(err, null, 0);
                        }

                        if (bytesRead === 0) {
                            return end(null, buffer, offset);
                        }

                        offset += bytesRead;

                        if (offset === totalBytes) {
                            return end(null, buffer, offset);
                        }

                        return readChunk();
                    });
                }

                readChunk();
            });
        });
    }
    detectEncodingByBOMFromBuffer(buffer: NodeBuffer, bytesRead: number): string {
        if (!buffer || bytesRead < 2) {
            return null;
        }

        const b0 = buffer.readUInt8(0);
        const b1 = buffer.readUInt8(1);

        // UTF-16 BE
        if (b0 === 0xFE && b1 === 0xFF) {
            return "utf16be";
        }

        // UTF-16 LE
        if (b0 === 0xFF && b1 === 0xFE) {
            return "utf16le";
        }

        if (bytesRead < 3) {
            return null;
        }

        const b2 = buffer.readUInt8(2);

        // UTF-8
        if (b0 === 0xEF && b1 === 0xBB && b2 === 0xBF) {
            return "utf8";
        }

        return null;
    }
}